import re, json, logging
from django.db import DatabaseError
from django.shortcuts import redirect, render
from django.urls import reverse
from django.views import View
from django import http
from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.mixins import LoginRequiredMixin
from . import models
from users.utils import generate_verify_email_url, check_verify_email_token
from shop.utils.response_code import RETCODE
from shop.utils.views import LoginRequiredJson
from django_redis import get_redis_connection
from celery_tasks.email.tasks import send_verify_email

# Create your views here.

#创建日志输出器
logger = logging.getLogger('django')

class RegisterView(View):
    #获取注册html页面
    def get(self,request):
        return render(request,'register.html')
    #实现注册逻辑
    def post(self,request):
        #接收参数 校验结果 保存数据 响应结果
        #判断参数是否齐全
        #前端校验，后端也得校验，防止恶意请求
        #用户名是5-20个字符
        #密码是8-20个字符
        #两次密码相同
        #判断手机号为11位
        #判断是否勾选协议
        p = request.POST
        username = p.get('username')
        password= p.get('password')
        password2 = p.get('password2')
        mobile = p.get('mobile')
        allow = p.get('allow')
        
        #短信验证码
        sms_code_client = p.get('sms_code')
        redis_con = get_redis_connection('verification_code')
        sms_code_server = redis_con.get('sms_%s' % mobile)
        sms_code_server = sms_code_server.decode()
        if sms_code_server is None:
            return render(request,'register.html',{'sms_code_errmsg': '短信验证码失效'})
        if sms_code_server != sms_code_client:
            return render(request,'register.html',{'sms_code_errmsg': '短信验证码错误'})
        
        #all方法，只要列表中有一个为null，返回false
        if not all([username,password,password2,mobile,allow]):
            #拒绝请求
            return http.HttpResponseForbidden('缺少必传参数')
        
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$',username):
            return http.HttpResponseForbidden('请输入5-20个字符的用户名，只能包含大小写字符 _-')
        
        if not re.match(r'^[0-9A-Za-z]{8,20}$',password):
            return http.HttpResponseForbidden('请输入8-20个字符的密码，只能包含大小写字符和数字')
        
        if password != password2:
            return http.HttpResponseForbidden('两次密码输入不一致')
        
        if not re.match(r'^1[3-9]\d{9}$',mobile):
            return http.HttpResponseForbidden('手机号错误')
        
        if allow != 'on':
            return http.HttpResponseForbidden('请勾选协议')
        
        #保存数据
        try:
            u = models.User.objects.create_user(username=username,password=password,mobile=mobile)
        except DatabaseError:
            #失败则响应错误信息,回到注册界面
            return render(request,'registser.html',{'register_errmsg': '注册失败'})
        
        #状态保持，这里django自带的auth认证模块会将注册信息保存在cookie和session中
        login(request,u)
        
        #重定向到首页
        path = reverse('contents:index')
        response =  redirect(path)
        response.set_cookie('username',u.username,3600 * 24 * 14)
        
        return response
    
#判断用户名是否重复，返回json数据
class UserNameCount(View):
    def get(self,request,username):
        #获取结果集条数
        count = models.User.objects.filter(username=username).count()
        #以json为格式，配合ajax使用
        return http.JsonResponse(
            {
                'code':  RETCODE.OK,
                'errmsg': 'OK',
                'count': count
            }
        )
        
#判断手机号是否重复
class MobileCountView(View):
    def get(self,request,mobile):
        count = models.User.objects.filter(mobile=mobile).count()
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'OK',
            'count': count
        })

#用户登录 
class LoginView(View):
    #跳转用户登录界面
    def get(self,request):
        
        return render(request,'login.html')
    
    #接收表单提交，实现登录逻辑
    def post(self,request):
        #接收参数
        r = request.POST
        username = r.get('username')
        password = r.get('password')
        remembered = r.get('remembered')
        #校验参数
        if not all([username,password]):
            return http.HttpResponseForbidden('缺少参数')
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$',username):
            return http.HttpResponseForbidden('用户名输入格式不符')
        if not re.match(r'^[0-9A-Za-z]{8,20}$',password):
            return http.HttpResponseForbidden('密码输入格式不符')
        #认证用户是否存在，密码是否正确
        #auth提供了一键查询账号的功能，底层采用的就是get查询username，然后调用check_password判断密码是否正确
        user = authenticate(username=username,password=password)
        if user is None:
            return render(request,'login.html',{'account_errmsg': '账号或密码错误'})
        #状态保持
        login(request,user)
        #使用remembered确定状态要保持多久
        if remembered != 'on':
            #没有记住登录
            request.session.set_expiry(0)
        else:
            #记住登录，保持两周,默认是两周，或者填入参数，单位为秒
            request.session.set_expiry(None)
        
        #登录完成确定 next参数，如果有next参数，就跳转到next指向的地址,从哪来回哪去
        next = request.GET.get('next')
        
        if next:
            return redirect(next)
        else:
            path = reverse('contents:index')
            #为了实现在首页展示用户名信息，需要将用户名缓存在cookie中
            response = redirect(path)
            response.set_cookie('username',user.username,max_age=3600 * 24 * 14)
            #响应结果
            return response
    
    
class LogoutView(View):
    def get(self,request):
        #实现用户退出登录
        logout(request)
        #删除cookie中的数据
        response = redirect(reverse('contents:index'))
        response.delete_cookie('username')
        
        return response
    
#用户中心，继承LoginRequiredMixin，帮助完成用户认证
#setting文件中LOGIN_URL确定用户未登录跳转地址
class UserInfoView(LoginRequiredMixin,View):
    
    def get(self,request):
        u = request.user
        context = {
            'username': u.username,
            'mobile': u.mobile,
            'email': u.email,
            'email_is_active': u.email_is_active
        }
        return render(request,'user_center_info.html',context)
    

#用户邮箱认证绑定,用户认证不能采用LoginRequiredMixin
#因为LoginRequiredMixin默认未登录时采用的是跳转到指定路由方式
#需要重写 handle_no_permission 方法 
class EmailView(LoginRequiredJson,View):
    def put(self,request):
        #接收参数
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        email = json_dict.get('email')
        
        #校验参数
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$',email):
            return http.HttpResponseForbidden('电子邮件格式有误')
        
        #将email存入当前数据库用户记录中
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({
                'code': RETCODE.DBERR,
                'errmsg': '添加邮箱失败'
            })
        
        #发送验证邮件,生成用戶唯一url
        verify_url = generate_verify_email_url(request.user)
        
        send_verify_email.delay(email,verify_url)
        
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': '添加邮箱成功'
        })

#校验邮箱
class VerifyEmail(View):
    def get(self,request):
        token = request.GET.get('token')
        if not token:
            return http.HttpResponseForbidden('请求缺少token')
        
        user = check_verify_email_token(token)
        
        if not user:
            return http.HttpResponseBadRequest('无效token')
        
        user.email_is_active = True
        
        try:
            user.save()
        except Exception as e:
            logger.error(e)
            return http.HttpResponseServerError('激活邮箱失败')
        
        return redirect(reverse('users:info'))

#用户收货地址
class AddressView(LoginRequiredMixin,View):
    def get(self,request):
        #查询用户所有地址信息，用于前端渲染
        #同时前端采用vue2进行渲染，不采用jinja2模版进行渲染
        #所有查询出的模型列表要转换为字典列表
        
        u = request.user
        #只查询is_deleted为False的地址，防止逻辑删除后依旧出现
        addresses = models.Address.objects.filter(user=u,is_deleted=False)
        address_list = []
        for address in addresses:
            addresses_dict = {
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
                
            }
            address_list.append(addresses_dict)
            
        #响应数据
        context={
            'default_address_id': u.default_address_id,
            'addresses': address_list
        }
        
        return render(request,'user_center_site.html',context)
    
    
#使用表单创建地址
class AddressCreateView(LoginRequiredJson,View):
    def post(self,request):
        #查询当前登录用户拥有多少地址
        #count = models.Address.objects.filter(user=request.user).count()
        count = request.user.addresses.count()
        if count > 20:
            return http.JsonResponse({
                'code': RETCODE.THROTTLINGERR,
                'errmsg': '超出用户地址存储上线'
            })
        
        
        #接收参数,将二进制数据转换成JSON
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')
        #校验参数
        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')
        
        #保存传入的地址信息
        try:
            address = models.Address.objects.create(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
            
        #为当前用户设定默认地址
            if not request.user.default_address:
                request.user.default_address = address
                request.user.save()
                
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({
                'code': RETCODE.DBERR,
                'errmsg': '新增地址失败'
            })
        
        #新增地址数据
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        
        
        #响应结果
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errsmg': '新增地址成功',
            'address': address_dict
        })

#修改和删除地址
class UpdateDestoryAddressView(LoginRequiredJson,View):
    #更新地址
    def put(self,request,address_id):
        #接收参数,将二进制数据转换成JSON
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')
        
        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')
            
        #使用最新的地址信息覆盖旧的地址信息
        try:
            #这里返回的是受影响的行数
            models.Address.objects.filter(id=address_id).update(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({
                'code': RETCODE.DBERR,
                'errsmg': '修改地址失败',
            })
        
        address = models.Address.objects.get(id=address_id)
        
        #响应
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errsmg': '修改地址成功',
            'address': address_dict
        })
        
    #删除地址
    def delete(self,request,address_id):
        #逻辑删除，将is_deleted = True
        #响应结果
        address = models.Address.objects.get(id=address_id)
        address.is_deleted = True
        address.save()
        
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': '删除地址成功'
        })
        
#设置默认地址
class DefaultAddressView(LoginRequiredJson,View):
    def put(self,request,address_id):
        address = models.Address.objects.get(id=address_id)
        request.user.default_address = address
        request.user.save()
        
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': '设置默认地址成功'
        })
        
#修改标题
class UpdateTitleView(LoginRequiredJson,View):
    def put(self,request,address_id):
        address = models.Address.objects.get(id=address_id)
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        title = json_dict.get('title')
        address.title = title

        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': '修改标题成功'
        })
        
#返回修改密码界面
class PasswordView(LoginRequiredMixin,View):
    def get(self,request):
        return render(request,'user_center_pass.html')
    
    
#修改密码后端逻辑
class UpdatePasswordView(LoginRequiredMixin,View):
    def post(self,request):
        #获取数据
        old_password = request.POST.get('old_password')
        new_password = request.POST.get('new_password')
        #这里其实需要校验数据，防止黑客跳过前端校验直接发送post请求
        
        #判断一下旧密码是否正确
        if request.user.check_password(old_password):
            #旧密码正确
            request.user.set_password(new_password)
            request.user.save()
            
             #重定向到首页
            path = reverse('contents:index')
            response =  redirect(path)
            return response
        else:
            
            return render(request,'user_center_pass.html',{
                'origin_password_errmsg': '当前密码输入错误'   
            })